

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/favicon.png">
  <link rel="icon" href="/img/favicon.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="Cai Shibo">
  <meta name="keywords" content="cc">
  
    <meta name="description" content="MySQLACID 原子性（Atomicity） ： 事务是最小的执行单位，不允许分割。事务的原子性确保动作要么全部完成，要么完全不起作用；undo log(回滚日志) 一致性（Consistency）： 执行事务前后，数据保持一致，例如转账业务中，无论事务是否成功，转账者和收款人的总额应该是不变的；靠其他三个特性保障 隔离性（Isolation）： 并发访问数据库时，一个用户的事务不被其他事务">
<meta property="og:type" content="article">
<meta property="og:title" content="MySQL&amp;Redis&amp;Spring">
<meta property="og:url" content="https://hahsx_xd.gitee.io/2022/04/07/MySQL/index.html">
<meta property="og:site_name" content="🍊CAI SHIBO🥬">
<meta property="og:description" content="MySQLACID 原子性（Atomicity） ： 事务是最小的执行单位，不允许分割。事务的原子性确保动作要么全部完成，要么完全不起作用；undo log(回滚日志) 一致性（Consistency）： 执行事务前后，数据保持一致，例如转账业务中，无论事务是否成功，转账者和收款人的总额应该是不变的；靠其他三个特性保障 隔离性（Isolation）： 并发访问数据库时，一个用户的事务不被其他事务">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2022-04-07T10:28:06.000Z">
<meta property="article:modified_time" content="2022-11-29T07:14:46.483Z">
<meta property="article:author" content="Cai Shibo">
<meta property="article:tag" content="cc">
<meta name="twitter:card" content="summary_large_image">
  
  
  <title>MySQL&amp;Redis&amp;Spring - 🍊CAI SHIBO🥬</title>

  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4/dist/css/bootstrap.min.css" />


  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@4/github-markdown.min.css" />
  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hint.css@2/hint.min.css" />

  
    
    
      
      <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@10/styles/github-gist.min.css" />
    
  

  
    <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3/dist/jquery.fancybox.min.css" />
  


<!-- 主题依赖的图标库，不要自行修改 -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_ba1fz6golrf.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/css/main.css" />

<!-- 自定义样式保持在最底部 -->


  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    var CONFIG = {"hostname":"hahsx_xd.gitee.io","root":"/","version":"1.8.14","typing":{"enable":true,"typeSpeed":70,"cursorChar":".|","loop":false},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"right","visible":"hover","icon":"#"},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"copy_btn":true,"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":5},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":true,"offset_factor":2},"web_analytics":{"enable":false,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/local-search.xml"};
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
<meta name="generator" content="Hexo 5.4.0"><link rel="alternate" href="/atom.xml" title="🍊CAI SHIBO🥬" type="application/atom+xml">
</head>


<body>
  <header style="height: 70vh;">
    <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/">
      <strong>^_^ 🥬 CC</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              &nbsp;<i class="iconfont icon-search"></i>&nbsp;
            </a>
          </li>
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

    <div class="banner" id="banner" parallax=true
         style="background: url('/img/default.png') no-repeat center center;
           background-size: cover;">
      <div class="full-bg-img">
        <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
          <div class="page-header text-center fade-in-up">
            <span class="h2" id="subtitle" title="MySQL&amp;Redis&amp;Spring">
              
            </span>

            
              <div class="mt-3">
  
    <span class="post-meta mr-2">
      <i class="iconfont icon-author" aria-hidden="true"></i>
      Cai Shibo
    </span>
  
  
    <span class="post-meta">
      <i class="iconfont icon-date-fill" aria-hidden="true"></i>
      <time datetime="2022-04-07 18:28" pubdate>
        2022年4月7日 晚上
      </time>
    </span>
  
</div>

<div class="mt-1">
  
    <span class="post-meta mr-2">
      <i class="iconfont icon-chart"></i>
      8.7k 字
    </span>
  

  
    <span class="post-meta mr-2">
      <i class="iconfont icon-clock-fill"></i>
      
      
      44 分钟
    </span>
  

  
  
    
      <!-- 不蒜子统计文章PV -->
      <span id="busuanzi_container_page_pv" style="display: none">
        <i class="iconfont icon-eye" aria-hidden="true"></i>
        <span id="busuanzi_value_page_pv"></span> 次
      </span>
    
  
</div>

            
          </div>

          
        </div>
      </div>
    </div>
  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="d-none d-lg-block col-lg-2"></div>
    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div class="py-5" id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">MySQL&amp;Redis&amp;Spring</h1>
            
              <p class="note note-info">
                
                  最后更新：23 天前
                
              </p>
            
            <div class="markdown-body">
              <hr>
<h3 id="MySQL"><a href="#MySQL" class="headerlink" title="MySQL"></a>MySQL</h3><h4 id="ACID"><a href="#ACID" class="headerlink" title="ACID"></a>ACID</h4><ul>
<li>原子性（Atomicity） ： 事务是最小的执行单位，不允许分割。事务的原子性确保动作要么全部完成，要么完全不起作用；<strong>undo log(回滚日志)</strong></li>
<li>一致性（Consistency）： 执行事务前后，数据保持一致，例如转账业务中，无论事务是否成功，转账者和收款人的总额应该是不变的；<strong>靠其他三个特性保障</strong></li>
<li>隔离性（Isolation）： 并发访问数据库时，一个用户的事务不被其他事务所干扰，各并发事务之间数据库是独立的；<strong>锁机制、MVCC 等手段</strong></li>
<li>持久性（Durability）： 一个事务被提交之后。它对数据库中数据的改变是持久的，即使数据库发生故障也不应该对其有任何影响。**redo log(重做日志) **</li>
</ul>
<hr>
<h4 id="MySAM、InnoDB"><a href="#MySAM、InnoDB" class="headerlink" title="MySAM、InnoDB"></a>MySAM、InnoDB</h4><ol>
<li><p>是否支持行级锁<br>MyISAM 只有表级锁(table-level locking)，而 InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。所以说 MyISAM 一锁就是锁住了整张表，因此在并发写的情况下 InnoDB 性能更强。</p>
</li>
<li><p>是否支持事务<br>MyISAM 不提供事务支持。InnoDB 提供事务支持，具有提交(commit)和回滚(rollback)事务的能力。</p>
</li>
<li><p>是否支持外键<br>MyISAM 不支持，而 InnoDB 支持。<br>一般不建议在数据库层面使用外键的，应用层面可以解决。不过，这样会对数据的一致性造成威胁。具体要不要使用外键还是要根据你的项目来决定。</p>
</li>
<li><p>是否支持数据库异常崩溃后的安全恢复<br>MyISAM 不支持，而 InnoDB 支持。<br>使用 InnoDB 的数据库在异常崩溃后，数据库重新启动的时候会保证数据库恢复到崩溃前的状态。这个恢复的过程依赖于 redo log 。<br>MySQL InnoDB 引擎使用 redo log(重做日志) 保证事务的持久性，使用 undo log(回滚日志) 来保证事务的原子性。<br>MySQL InnoDB 引擎通过 锁机制、MVCC 等手段来保证事务的隔离性（ 默认支持的隔离级别是 REPEATABLE-READ ）。<br>保证了事务的持久性、原子性、隔离性之后，一致性才能得到保障。</p>
</li>
<li><p>是否支持 MVCC<br>MyISAM 不支持，而 InnoDB 支持。<br>MVCC 可以看作是行级锁的一个升级，可以有效减少加锁操作，提高性能。</p>
</li>
</ol>
<hr>
<h4 id="锁机制（InnoDB）"><a href="#锁机制（InnoDB）" class="headerlink" title="锁机制（InnoDB）"></a>锁机制（InnoDB）</h4><p>Record lock：记录锁，单个行记录上的锁<br>Gap lock：间隙锁，锁定一个范围，不包括记录本身<br>Next-key lock：record+gap 临键锁，锁定一个范围，包含记录本身</p>
<hr>
<h4 id="并发事物问题-脏读…"><a href="#并发事物问题-脏读…" class="headerlink" title="并发事物问题 (脏读…)"></a>并发事物问题 (脏读…)</h4><ul>
<li>脏读（Dirty read）: 当一个事务正在访问数据并且对数据进行了修改，而这种修改还没有提交到数据库中，这时另外一个事务也访问了这个数据，然后使用了这个数据。因为这个数据是还没有提交的数据，那么另外一个事务读到的这个数据是“脏数据”，依据“脏数据”所做的操作可能是不正确的。</li>
<li>丢失修改（Lost to modify）: 指在一个事务读取一个数据时，另外一个事务也访问了该数据，那么在第一个事务中修改了这个数据后，第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失，因此称为丢失修改。 例如：事务 1 读取某表中的数据 A=20，事务 2 也读取 A=20，事务 1 修改 A=A-1，事务 2 也修改 A=A-1，最终结果 A=19，事务 1 的修改被丢失。</li>
<li>不可重复读（Unrepeatable read）: 指在一个事务内多次读同一数据。在这个事务还没有结束时，另一个事务也访问该数据。那么，在第一个事务中的两次读数据之间，由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况，因此称为不可重复读。</li>
<li>幻读（Phantom read）: 幻读与不可重复读类似。它发生在一个事务（T1）读取了几行数据，接着另一个并发事务（T2）插入了一些数据时。在随后的查询中，第一个事务（T1）就会发现多了一些原本不存在的记录，就好像发生了幻觉一样，所以称为幻读。</li>
</ul>
<h4 id="隔离级别"><a href="#隔离级别" class="headerlink" title="隔离级别"></a>隔离级别</h4><ul>
<li>READ-UNCOMMITTED(读取未提交)：最低的隔离级别，允许读取尚未提交的数据变更，可能会导致<strong>脏读、幻读或不可重复读</strong>。</li>
<li>READ-COMMITTED(读取已提交)：允许读取并发事务已经提交的数据，可以<strong>阻止脏读，但是幻读或不可重复读仍有可能发生</strong>。</li>
<li>REPEATABLE-READ(可重复读)：对同一字段的多次读取结果都是一致的，除非数据是被本身事务自己所修改，可以<strong>阻止脏读和不可重复读，但幻读仍有可能发生</strong>。</li>
<li>SERIALIZABLE(可串行化)：最高的隔离级别，完全服从 ACID 的隔离级别。所有的事务依次逐个执行，这样事务之间就完全不可能产生干扰，也就是说，该级别可以<strong>防止脏读、不可重复读以及幻读</strong>。</li>
</ul>
<h4 id="默认隔离级别"><a href="#默认隔离级别" class="headerlink" title="默认隔离级别"></a>默认隔离级别</h4><p>InnoDB 的 REPEATABLE-READ（<strong>可重读</strong>）并不保证避免幻读，需要应用使用加锁读来保证。而这个加锁读使用到的机制就是 Next-Key Locks。</p>
<p>因为隔离级别越低，事务请求的锁越少，所以大部分数据库系统的隔离级别都是 READ-COMMITTED(读取已提交) ，但是 InnoDB 存储引擎默认使用 REPEATABLE-READ（可重读） 并<strong>不会有任何性能损失</strong>。</p>
<p>InnoDB 存储引擎在 分布式事务 的情况下一般会用到 SERIALIZABLE(可串行化) 隔离级别。</p>
<hr>
<h4 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h4><p>索引是一种用于快速查询和检索数据的数据结构。常见的索引结构有: B 树， B+树和 Hash。</p>
<p>优点 ：</p>
<ul>
<li>使用索引可以大大加快 数据的检索速度（大大减少检索的数据量）, 这也是创建索引的最主要的原因。</li>
<li>通过创建唯一性索引，可以保证数据库表中每一行数据的唯一性。</li>
</ul>
<p>缺点 ：</p>
<ul>
<li>创建索引和维护索引需要耗费许多时间。当对表中的数据进行增删改的时候，如果数据有索引，那么索引也需要动态的修改，会降低 SQL 执行效率。</li>
<li>索引需要使用物理文件存储，也会耗费一定空间。</li>
</ul>
<h5 id="Hash存在问题"><a href="#Hash存在问题" class="headerlink" title="Hash存在问题"></a>Hash存在问题</h5><ol>
<li>Hash 冲突问题 ：我们上面也提到过Hash 冲突了，不过对于数据库来说这还不算最大的缺点。</li>
<li>Hash 索引不支持顺序和范围查询(Hash 索引不支持顺序和范围查询是它最大的缺点： 假如我们要对表中的数据进行排序或者进行范围查询，那 Hash 索引可就不行了。</li>
</ol>
<h5 id="B、B-树"><a href="#B、B-树" class="headerlink" title="B、B+树"></a>B、B+树</h5><ul>
<li>B 树的所有节点既存放键(key) 也存放 数据(data)，而 B+树只有叶子节点存放 key 和 data，其他内节点只存放 key。</li>
<li>B 树的叶子节点都是独立的; B+树的叶子节点有一条引用链指向与它相邻的叶子节点。</li>
<li>B 树的检索的过程相当于对范围内的每个节点的关键字做二分查找，可能还没有到达叶子节点，检索就结束了。而 B+树的检索效率就很稳定了，任何查找都是从根节点到叶子节点的过程，叶子节点的顺序检索很明显。</li>
</ul>
<h5 id="为什么不用B树"><a href="#为什么不用B树" class="headerlink" title="为什么不用B树"></a>为什么不用B树</h5><ul>
<li>因为B树每个节点都有数据，这就导致如果数据很大，在找到目标数据前需要加载大量的无用数据。只加载键极大减少了资源浪费，提高索引效率。</li>
<li>由于每个节点都存储了实际数据，导致其需要的空间更大，每个节点可以存的记录数更多，B+树高度更低，访问所需IO次数更少。</li>
<li>范围查找需要找到最小值，再进行中序遍历，不如B+树简单。</li>
</ul>
<h5 id="MySAM、InnoDB树区别"><a href="#MySAM、InnoDB树区别" class="headerlink" title="MySAM、InnoDB树区别"></a>MySAM、InnoDB树区别</h5><ul>
<li>MySAM叶子节点存的数据是记录的地址（非聚簇索引），InnoDB存的是真实的数据（聚簇索引）。</li>
<li>辅助索引：InnoDB叶子节点存的数据是该记录的主键；MySAM仍然是记录地址。所以InnoDB辅助索引找到后还要根据主键去找数据。</li>
</ul>
<h5 id="非-聚簇索引"><a href="#非-聚簇索引" class="headerlink" title="非/聚簇索引"></a>非/聚簇索引</h5><p><strong>聚集索引</strong><br>聚集索引即索引结构和数据一起存放的索引。主键索引属于聚集索引。</p>
<p><strong>聚集索引的优点</strong><br>聚集索引的查询速度非常的快，因为整个 B+树本身就是一颗多叉平衡树，叶子节点也都是有序的，定位到索引的节点，就相当于定位到了数据。</p>
<p><strong>聚集索引的缺点</strong>  </p>
<ul>
<li>依赖于有序的数据 ：因为 B+树是多路平衡树，如果索引的数据不是有序的，那么就需要在插入时排序，如果数据是整型还好，否则类似于字符串或 UUID 这种又长又难比较的数据，插入或查找的速度肯定比较慢。  </li>
<li>更新代价大 ： 如果对索引列的数据被修改时，那么对应的索引也将会被修改，而且聚集索引的叶子节点还存放着数据，修改代价肯定是较大的，所以对于主键索引来说，主键一般都是不可被修改的。</li>
</ul>
<p><strong>非聚集索引</strong><br>非聚集索引即索引结构和数据分开存放的索引。<strong>二级索引属于非聚集索引。</strong>非聚集索引的叶子节点并不一定存放数据的指针，因为二级索引的叶子节点就存放的是主键，根据主键再回表查数据。</p>
<p><strong>非聚集索引的优点</strong> </p>
<ul>
<li>更新代价比聚集索引要小 。非聚集索引的更新代价就没有聚集索引那么大了，非聚集索引的叶子节点是不存放数据的</li>
</ul>
<p><strong>非聚集索引的缺点</strong> </p>
<ul>
<li>跟聚集索引一样，非聚集索引也依赖于有序的数据</li>
<li>可能会二次查询(回表) :这应该是非聚集索引最大的缺点了。 当查到索引对应的指针或主键后，可能还需要根据指针或主键再到数据文件或表中查询。</li>
</ul>
<p>**接JavaGuide整理-1，本文主要为Redis、Netty、Spring等框架内容。</p>
<span id="more"></span>

<hr>
<h3 id="Redis"><a href="#Redis" class="headerlink" title="Redis"></a>Redis</h3><h4 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h4><p>Redis 就是一个<strong>使用 C 语言开发的数据库</strong>，不过与传统数据库不同的是 Redis 的数据是存在内存中的 ，也就是它是<strong>内存数据库，所以读写速度非常快</strong>，因此 Redis 被广泛应用于缓存方向。除此之外还经常用来做分布式锁，消息队列。Redis 还支持事务 、持久化、Lua 脚本、多种集群方案。</p>
<h5 id="业务场景"><a href="#业务场景" class="headerlink" title="业务场景"></a>业务场景</h5><ul>
<li>分布式锁 ： 通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下，我们都是基于 Redisson 来实现分布式锁。</li>
<li>限流 ：一般是通过 Redis + Lua 脚本的方式来实现限流。</li>
<li>消息队列 ：Redis 自带的 list 数据结构可以作为一个简单的队列使用。Redis5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka，有主题和消费组的概念，支持消息持久化以及 ACK 机制。</li>
<li>复杂业务场景 ：通过 Redis 以及 Redis 扩展（比如 Redisson）提供的数据结构，我们可以很方便地完成很多复杂的业务场景比如<strong>通过 bitmap 统计活跃用户、通过 sorted set 维护排行榜</strong>。</li>
</ul>
<h4 id="为什么快-原理"><a href="#为什么快-原理" class="headerlink" title="为什么快/原理"></a>为什么快/原理</h4><ul>
<li>基于内存操作；</li>
<li>Redis 通过IO 多路复用程序 来监听来自客户端的大量连接（或者说是监听多个 socket），它会将感兴趣的事件及类型（读、写）注册到内核中并监听每个事件是否发生。I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接，降低了资源的消耗（和 NIO 中的 Selector 组件很像）。</li>
</ul>
<h4 id="单线程模型及原因"><a href="#单线程模型及原因" class="headerlink" title="单线程模型及原因"></a>单线程模型及原因</h4><p>Redis 的单线程指的是<strong>Redis 的网络 IO （6.x 版本后网络 IO 使用多线程）以及键值对指令读写是由一个线程来执行的</strong>。对于 Redis 的<strong>持久化、集群数据同步、异步删除等都是其他线程执行</strong>。</p>
<ul>
<li>Redis 的性能瓶颈不在 CPU ，主要在内存和网络。</li>
<li>上下文切换需要资源，消耗性能</li>
<li>避免线程竞争及可能产生的死锁，代码逻辑更清晰</li>
</ul>
<p>Redis6.0引入多线程主要是为了提高网络IO读写性能。</p>
<h4 id="判、删过期数据"><a href="#判、删过期数据" class="headerlink" title="判、删过期数据"></a>判、删过期数据</h4><p>过期字典，key指向Redis内存的数据的某个key，value是过期时间。</p>
<p>惰性删除和定期删除结合。</p>
<ul>
<li>惰性删除 ：只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好，但是可能会造成太多过期 key 没有被删除。</li>
<li>定期删除 ： 每隔一段时间抽取一批 key 执行删除过期 key 操作。并且，Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。</li>
</ul>
<h4 id="内存淘汰机制"><a href="#内存淘汰机制" class="headerlink" title="内存淘汰机制"></a>内存淘汰机制</h4><ol>
<li>volatile-ttl：从已设置过期时间的数据（server.db[i].expires）中挑选将要过期的数据淘汰</li>
<li>volatile-lru（least recently used）：从已设置过期时间的数据集（server.db[i].expires）中挑选最近最少使用的数据淘汰</li>
<li>volatile-random：从已设置过期时间的数据集（server.db[i].expires）中任意选择数据淘汰</li>
<li>volatile-lfu（least frequently used）：从已设置过期时间的数据集（server.db[i].expires）中挑选最不经常使用的数据淘汰</li>
<li><strong>allkeys-lru（least recently used）：当内存不足以容纳新写入数据时，在键空间中，移除最近最少使用的 key（这个是最常用的）</strong></li>
<li>allkeys-random：从数据集（server.db[i].dict）中任意选择数据淘汰</li>
<li>allkeys-lfu（least frequently used）：当内存不足以容纳新写入数据时，在键空间中，移除最不经常使用的 key</li>
<li><em>no-eviction：禁止驱逐数据，也就是说当内存不足以容纳新写入数据时，新写入操作会报错。这个应该没人使用吧！</em></li>
</ol>
<h4 id="Redis持久化机制"><a href="#Redis持久化机制" class="headerlink" title="Redis持久化机制"></a>Redis持久化机制</h4><ul>
<li>快照 RDB：存储某个时间节点的数据副本，可以快速备份快照，从而创建相同数据的服务器副本（Redis主从结构，提高性能）；重启服务器也可以使用快照恢复数据。<figure class="highlight plaintext"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs in">save 900 1           #在900秒(15分钟)之后，如果至少有1个key发生变化，Redis就会自动触发BGSAVE命令创建快照。<br><br>save 300 10          #在300秒(5分钟)之后，如果至少有10个key发生变化，Redis就会自动触发BGSAVE命令创建快照。<br><br>save 60 10000        #在60秒(1分钟)之后，如果至少有10000个key发生变化，Redis就会自动触发BGSAVE命令创建快照。<br></code></pre></div></td></tr></table></figure></li>
<li>只追加文件 AOF（默认关闭）：每执行一条数据更改命令，就会将命令写到内存缓存server.aof_buf 中，然后再根据 appendfsync 配置来决定何时将其同步到硬盘中的 AOF 文件。everysec方式几乎不会影响 Redis 性能，且最多丢失一秒的数据。    <figure class="highlight nginx"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs nginx">   <span class="hljs-attribute">appendonly</span> <span class="hljs-literal">yes</span>     <span class="hljs-comment">#开启AOF</span><br> <span class="hljs-comment">#AOF 三种持久化方式</span><br>   ppendfsync always     <span class="hljs-comment">#每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度</span><br>appendfsync everysec  <span class="hljs-comment">#每秒钟同步一次，显式地将多个写命令同步到硬盘</span><br>appendfsync <span class="hljs-literal">no</span>        <span class="hljs-comment">#让操作系统决定何时进行同步</span><br></code></pre></div></td></tr></table></figure></li>
</ul>
<h4 id="缓存和数据库一致"><a href="#缓存和数据库一致" class="headerlink" title="缓存和数据库一致"></a>缓存和数据库一致</h4><p>旁路缓存模式：<br><strong>1、更新 DB； 2、直接删除 cache</strong>  </p>
<p>删除失败的解决方案：</p>
<ul>
<li>缓存失效时间变短（不推荐，治标不治本） ：我们让缓存数据的过期时间变短，这样的话缓存就会从数据库中加载数据。另外，这种解决办法对于先操作缓存后操作数据库的场景不适用。</li>
<li>增加 cache 更新重试机制（常用）： 如果 cache 服务当前不可用导致缓存删除失败的话，我们就隔一段时间进行重试，重试次数可以自己定。如果多次重试还是失败的话，我们可以把当前更新失败的 key 存入队列中，等缓存服务可用之后，再将缓存中对应的 key 删除即可。</li>
</ul>
<hr>
<h4 id="Redis-和-Memcached"><a href="#Redis-和-Memcached" class="headerlink" title="Redis 和 Memcached"></a>Redis 和 Memcached</h4><ul>
<li>Redis 支持更丰富的数据类型（支持更复杂的应用场景）。Redis 不仅仅支持简单的 k/v 类型的数据，同时还提供 list，set，zset，hash 等数据结构的存储。Memcached 只支持最简单的 k/v 数据类型。</li>
<li>Redis 支持数据的持久化，可以将内存中的数据保持在磁盘中，重启的时候可以再次加载进行使用,而 Memcached 把数据全部存在内存之中。</li>
<li>Redis 有灾难恢复机制。 因为可以把缓存中的数据持久化到磁盘上。</li>
<li>Redis 在服务器内存使用完之后，可以将不用的数据放到磁盘上。但是，Memcached 在服务器内存使用完之后，就会直接报异常。</li>
<li>Memcached 没有原生的集群模式，需要依靠客户端来实现往集群中分片写入数据；但是 Redis 目前是原生支持 cluster 模式的。</li>
<li>Memcached 是多线程，非阻塞 IO 复用的网络模型；Redis 使用单线程的多路 IO 复用模型。 （Redis 6.0 引入了多线程 IO ）</li>
<li>Redis 支持发布订阅模型、Lua 脚本、事务等功能，而 Memcached 不支持。并且，Redis 支持更多的编程语言。</li>
<li>Memcached 过期数据的删除策略只用了惰性删除，而 Redis 同时使用了惰性删除与定期删除。</li>
</ul>
<hr>
<h3 id="Spring"><a href="#Spring" class="headerlink" title="Spring"></a>Spring</h3><h4 id="理解"><a href="#理解" class="headerlink" title="理解"></a>理解</h4><p>轻量级 Java 开发框架，旨在提高开发人员的开发效率以及系统的可维护性，最核心的思想是不重复造轮子，最核心的功能是 IoC（Inverse of Control:控制反转） 和 AOP(Aspect-Oriented Programming:面向切面编程）。</p>
<ul>
<li>Spring Core：核心模块， Spring 其他所有的功能基本都需要依赖于该类库，主要提供 IoC 依赖注入功能的支持。</li>
<li>Spring AOP：提供了面向切面的编程实现。</li>
<li>Spring Web由 4 个模块组成：  <ul>
<li>spring-web ：对 Web 功能的实现提供一些最基础的支持。  </li>
<li>spring-webmvc ： 提供对 Spring MVC 的实现。</li>
<li>spring-websocket ： 提供了对 WebSocket 的支持，WebSocket 可以让客户端和服务端进行双向通信。  </li>
<li>spring-webflux ：提供对 WebFlux 的支持。WebFlux 是 Spring Framework 5.0 中引入的新的响应式框架。与 Spring MVC 不同，它不需要 Servlet API，是完全异步。</li>
</ul>
</li>
</ul>
<h4 id="IOC"><a href="#IOC" class="headerlink" title="IOC"></a>IOC</h4><p>IoC（Inverse of Control:控制反转） 是一种设计思想，而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权，交由 Spring 框架来管理。</p>
<p>控制 ：指的是对象创建（实例化、管理）的权力<br>反转 ：控制权交给外部环境（Spring 框架、IoC 容器）</p>
<hr>
<h4 id="AOP"><a href="#AOP" class="headerlink" title="AOP"></a>AOP</h4><p>AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关，却为业务模块所共同调用的逻辑或责任（例如事务处理、日志管理、权限控制等）封装起来，便于减少系统的重复代码，降低模块间的耦合度，并有利于未来的可拓展性和可维护性。</p>
<p>Spring AOP 就是基于动态代理的，如果要代理的对象，实现了某个接口，那么 Spring AOP 会使用 JDK Proxy，去创建代理对象，而对于没有实现接口的对象，就无法使用 JDK Proxy 去进行代理了，这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。</p>
<h5 id="Spring-AOP-和-AspectJ-AOP"><a href="#Spring-AOP-和-AspectJ-AOP" class="headerlink" title="Spring AOP 和 AspectJ AOP"></a>Spring AOP 和 AspectJ AOP</h5><p>Spring AOP 属于<strong>运行时增强</strong>，而 AspectJ 是<strong>编译时增强</strong>。 Spring AOP **基于代理(Proxying)**，而 AspectJ **基于字节码操作(Bytecode Manipulation)**。</p>
<p>Spring AOP 已经集成了 AspectJ ，AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大，但是 Spring AOP 相对来说更简单，如果我们的切面比较少，那么两者性能差异不大。但是，<strong>当切面太多的话，最好选择 AspectJ ，它比 Spring AOP 快很多</strong>。</p>
<hr>
<h3 id="SpringBoot"><a href="#SpringBoot" class="headerlink" title="SpringBoot"></a>SpringBoot</h3><h4 id="SpringBootApplication注解"><a href="#SpringBootApplication注解" class="headerlink" title="@SpringBootApplication注解"></a>@SpringBootApplication注解</h4><p>等同于：</p>
<ul>
<li>@EnableAutoConfiguration：启动SpringBoot自动配置机制。</li>
<li>@ComponentScan：扫描被@Component注解的bean。</li>
<li>@Configuration：允许在上下文中注册额外的bean或导入其他配置类。</li>
</ul>
<h4 id="自动配置实现方式"><a href="#自动配置实现方式" class="headerlink" title="自动配置实现方式"></a>自动配置实现方式</h4><ol>
<li><strong>@EnableAutoConfiguration</strong> 通过 Spring 的 @Import 注解导入 AutoConfigurationImportSelector 类，</li>
<li>该类的 getCandidateConfiguration() 方法会将所有自动配置的类信息以 List 形式返回，这些配置信息会被 Spring 容器作 bean 来管理。</li>
<li>@Conditional 注解进行信息装配。</li>
</ol>
<hr>
<h3 id="Netty"><a href="#Netty" class="headerlink" title="Netty"></a>Netty</h3><h4 id="介绍-1"><a href="#介绍-1" class="headerlink" title="介绍"></a>介绍</h4><ol>
<li>Netty 是一个 基于 NIO 的 client-server(客户端服务器)框架，使用它可以快速简单地开发网络应用程序。</li>
<li>它极大地简化并优化了 TCP 和 UDP 套接字服务器等网络编程,并且性能以及安全性等很多方面甚至都要更好。</li>
<li>支持多种协议 如 FTP，SMTP，HTTP 以及各种二进制和基于文本的传统协议。</li>
<li>Netty找到了一种在不妥协可维护性和性能的情况下实现抑郁开发、性能、稳定性和灵活性的方法。</li>
</ol>
<h4 id="核心组件"><a href="#核心组件" class="headerlink" title="核心组件"></a>核心组件</h4><ul>
<li>字节容器：网络通信最终是通过字节流进行传输的。字节容器内部是一个字节数组，Netty通过其进行数据传输。</li>
<li>启动引导类：创建、引导客户端/服务端连接。</li>
<li>网络操作抽象类：进行IO操作</li>
</ul>
<h4 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h4><ul>
<li>实现自己的HTTP服务器（TomCat就是一个HTTP服务器）。</li>
<li>实现一个即时通信系统。</li>
<li>实现消息推送系统。</li>
</ul>
<h4 id="NioEventLoopGroup默认构造函数"><a href="#NioEventLoopGroup默认构造函数" class="headerlink" title="NioEventLoopGroup默认构造函数"></a>NioEventLoopGroup默认构造函数</h4><p>默认线程是：CPU核心数*2  </p>
<p>每个NioEventLoopGroup对象内部都会分配一组NioEventLoop，大小是nThreads，这样就构成了线程池，一个NioEventLoop和一个线程对应。</p>
<h4 id="Netty线程模型"><a href="#Netty线程模型" class="headerlink" title="Netty线程模型"></a>Netty线程模型</h4><p>一个线程</p>
<h4 id="NIO、Netty"><a href="#NIO、Netty" class="headerlink" title="NIO、Netty"></a>NIO、Netty</h4><ul>
<li>NIO的编程模型复杂，且存在bug，面对断连重连、包丢失等问题处理过程过于复杂。</li>
<li>Netty解决了这些问题。</li>
<li>Netty对比NIO、JDK自带的NIO API更易用，且有更低的延迟、资源消耗等。</li>
<li>统一的API，支持多种传输类型（阻塞、非阻塞）。</li>
<li>自带各种协议栈。</li>
</ul>
<hr>
<h3 id="Reactor线程模型"><a href="#Reactor线程模型" class="headerlink" title="Reactor线程模型"></a>Reactor线程模型</h3><p>是一种经典的线程模型，Reactor模式是机遇事件驱动，特别适合处理海量IO事件。</p>
<ul>
<li><p>单线程Reactor：所有IO操作都由同一个NIO处理。资源消耗小，无法支撑大量请求，处理请求时间长。</p>
</li>
<li><p>多线程：一个线程负责接收请求，一组NIO线程处理IO操作。百万并发一个线程负责接收存在性能问题。</p>
</li>
<li><p>主从多线程：一组线程负责接收请求，一组NIO线程处理IO操作。</p>
</li>
</ul>

            </div>
            <hr>
            <div>
              <div class="post-metas mb-3">
                
                
              </div>
              
                <p class="note note-warning">
                  
                    本博客所有文章未经允许，严禁转载！
                  
                </p>
              
              
                <div class="post-prevnext">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/2022/04/10/%E7%AE%97%E6%B3%95-%E9%93%BE%E8%A1%A8/">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">算法--链表</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2022/04/07/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/">
                        <span class="hidden-mobile">JVM、JDK新特性、计算机基础</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
          </article>
        </div>
      </div>
    </div>
    
      <div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn">
        <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>

      </div>
    
  </div>
</div>

<!-- Custom -->


    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v"
                 for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>
    

    
  </main>

  <footer class="text-center mt-5 py-3">
  <div class="footer-content">
     CC <i class="iconfont icon-love"></i> XX
  </div>
  
  <div class="statistics">
    
    

    
      
        <!-- 不蒜子统计PV -->
        <span id="busuanzi_container_site_pv" style="display: none">
            总访问量 
            <span id="busuanzi_value_site_pv"></span>
             次
          </span>
      
      
        <!-- 不蒜子统计UV -->
        <span id="busuanzi_container_site_uv" style="display: none">
            总访客数 
            <span id="busuanzi_value_site_uv"></span>
             人
          </span>
      
    
  </div>


  

  
</footer>


  <!-- SCRIPTS -->
  
  <script  src="https://cdn.jsdelivr.net/npm/nprogress@0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nprogress@0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js" ></script>
<script  src="https://cdn.jsdelivr.net/npm/bootstrap@4/dist/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>

<!-- Plugins -->


  <script  src="/js/local-search.js" ></script>



  
    
      <script  src="/js/img-lazyload.js" ></script>
    
  



  



  
    <script  src="https://cdn.jsdelivr.net/npm/tocbot@4/dist/tocbot.min.js" ></script>
  
  
    <script  src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3/dist/jquery.fancybox.min.js" ></script>
  
  
    <script  src="https://cdn.jsdelivr.net/npm/anchor-js@4/anchor.min.js" ></script>
  
  
    <script defer src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js" ></script>
  



  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>




  <script  src="https://cdn.jsdelivr.net/npm/typed.js@2/lib/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var title = document.getElementById('subtitle').title;
      
        typing(title);
      
    })(window, document);
  </script>















<!-- 主题的启动项 保持在最底部 -->
<script  src="/js/boot.js" ></script>


</body>
</html>
